﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework;

namespace BlackStar
{
    public class FontContentReader : ContentTypeReader<Font>
    {
        protected override Font Read(ContentReader input, Font existingInstance)
        {
            Font font = new Font();
            font.AssetName = input.ReadString();
            font.Load(input);
            return font;
        }
    }

    public class Font
    {
        public string AssetName;
        protected SpriteFont font;

        internal GUI gui;

        internal void Load(ContentReader input)
        {
            string assetDir = input.AssetName.Substring(0, input.AssetName.LastIndexOf("\\") + 1);
            font = input.ContentManager.Load<SpriteFont>(assetDir + this.AssetName);
        }

        protected float Scale(int size, string text)
        {
            // get the first line of the text.
            string toMeasure = (text.IndexOf("\n") == -1) ? text : text.Substring(0, text.IndexOf("\n"));

            Vector2 nativeSize = font.MeasureString(toMeasure);
            return size / nativeSize.Y;
        }

        public void Draw(string text, Vector2 position, int size, Color color)
        {
            if (text.Trim() == string.Empty) return;
            float scale = Scale(size, text);
            gui.batch.DrawString(font, text, position, color, 0, new Vector2(0, 0), scale, SpriteEffects.None, 0);
        }

        public void Draw(string text, Rectangle rect, int size, Color color)
        {
            if (text.Trim() == string.Empty) return;
            string shorttext;
            shorttext = TrimText(text, rect, size);
            float scale = Scale(size, shorttext);
            gui.batch.DrawString(font, shorttext, new Vector2(rect.X, rect.Y), color, 0, new Vector2(0, 0), scale, SpriteEffects.None, 0);
        }

        public string TrimText(string text, Rectangle rect, int size)
        {
            // currently only supports a single line.
            string result = text;
            
            Vector2 strSize = MeasureString(size, result);
            Vector2 interiorSize = new Vector2(rect.Width, rect.Height);

            while (strSize.X > interiorSize.X)
            {
                result = result.Substring(0, result.Length - 1);
                strSize = MeasureString(size, result);
            }
            return result;
        }

        public Vector2 MeasureString(int size, string text)
        {
            if (text == string.Empty) return new Vector2(0, 0);
            Vector2 result = font.MeasureString(text) * Scale(size, text);
            result.X = (float)Math.Ceiling(result.X);
            result.Y = (float)Math.Ceiling(result.Y);
            return result;
        }

        public void DrawCursor(int pos, string text, Rectangle rect, Color color, int size)
        {
            Vector2 vPos = MeasureString(size, text.Substring(0, pos));
            gui.DrawRectangle(new Rectangle(rect.X + (int)(vPos.X), rect.Y, 1, size), color, true);
        }

        public float CursorPos(int pos, string text, int size)
        {
            return (float)Math.Ceiling(MeasureString(size, text.Substring(0, pos)).X);
        }
         
        public int CursorPos(string text, Point pos, Rectangle rect, int size)
        {
            string strPart = text;

            if (pos.X >= rect.Right) return text.Length - 1;
            if (pos.X < rect.X) return -1;

            int clickLen = pos.X - rect.X;
            if (clickLen < 0) { return -1; }

            Vector2 nextLen = MeasureString(size, strPart.Substring(0, strPart.Length - 1));
            Vector2 len = MeasureString(size, strPart);

            while ((nextLen.X > (clickLen)) && (strPart.Length > 1))
            {
                strPart = strPart.Substring(0, strPart.Length - 1);
                nextLen = MeasureString(size, strPart.Substring(0, strPart.Length - 1));
                len = MeasureString(size, strPart);
            }

            // now we need to decide which is closer to pos, len or nextLen.
            float lenDiff = Math.Abs(clickLen - len.X);
            float nextDiff = Math.Abs(clickLen - nextLen.X);

            float result = (lenDiff < nextDiff) ? strPart.Length : strPart.Length - 1;
            return (int)Math.Ceiling(result);
        }
    }

    public class FontListContentReader : ContentTypeReader<FontList>
    {
        protected override FontList Read(ContentReader input, FontList existingInstance)
        {
            FontList list = new FontList();
            int count = input.ReadInt32();
            for (int i = 0; i < count; i++)
            { list.Add(input.ReadString(), input.ReadObject<Font>()); }

            if (!list.ContainsKey("Default"))
            { throw new ApplicationException("A Default font must be specified."); }

            return list;
        }
    }

    public class FontList : Dictionary<string, Font>
    {
        internal GUI myGui;

        public new Font this[string key]
        {
            get
            {
                if (this.ContainsKey(key))
                { return base[key]; }
                else
                { return base["Default"]; }
            }
            set { base[key] = value; }
        }

        [ContentSerializerIgnore]
        public GUI GUI
        {
            get { return myGui; }
            set 
            {
                myGui = value;
                foreach (KeyValuePair<string, Font> kvp in this)
                { kvp.Value.gui = value; }
            }
        }
    }
}
